#include <boost/spirit/core.hpp>
#include <boost/spirit/utility/lists.hpp>
#include <boost/spirit/actor/push_back_actor.hpp>
#include <iostream>
#include <fstream>
#include "compiler.hpp"
#include "parser.hpp"

struct todo_parser_t t;

using namespace std;
using namespace boost::spirit;

struct TodoGrammar : public grammar<TodoGrammar> {
	template <typename ScannerT>
	struct definition {
		definition(TodoGrammar const& self)
		{			
			// Types
			type = as_lower_d["int"] | as_lower_d["number"] | as_lower_d["double"] |
					as_lower_d["string"] | as_lower_d["bool"];

			// Identifier
			// [a-zA-Z][a-zA-Z0-9_]*
			ident = lexeme_d[+alpha_p >> *(alnum_p | '_')];

			// Scalar
			scalar = real_p[push_back_a(t.d)]
				|	('"' >> *(anychar_p - '"') >> '"')
				| 	as_lower_d["true"]
				| 	as_lower_d["false"];

			// Arithmetic operators
			arith_oper = (ch_p('+') | '-' | '/' | '%' | '*');
			
			// Logical operators
			logical_oper = as_lower_d["and"] | as_lower_d["or"] | as_lower_d["xor"] | "&&" | "||";
			
			// Comparison operators
			comp_oper = str_p("==") | "!=" | ">" | ">=" | "<" | "<=";
			
			// Bitwise operators
			bitwise_oper = str_p(">>") | "<<" | '|' | '^' | '&';
			
			// Unary operators
			unary_oper = ch_p('~') | '!';
			
			// Inc/Decr operator
			incdec_oper = str_p("++") | "--";
			
			// Operators
			oper = logical_oper | arith_oper | comp_oper  | bitwise_oper;

			// Operator expression
			oper_expr = ch_p('(') >> oper_expr >> *(oper >> oper_expr) >> ')' | value >> *(oper >> oper_expr);
			
			// Value for math expression
			value = (
					!unary_oper >> (scalar | func_call | ident)
				|	incdec_oper >> ident
				|	ident >> incdec_oper
			);
			
			// Argument list for calling
			args_call = !(value[push_back_a(t.args)] % ',');
			
			// Function call
			// func()
			// ns::foo->func()
			// ns::foo->class.method()
			func_call = ((ident >> '(' >> args_call >> ')' >> *('[' >> value >> ']'))[push_back_a(t.s)]
				|	((list_p(ident, "::") >> str_p("->") >> ident >> !(ch_p('.') >> ident))[push_back_a(t.s)]
							>> '(' >> args_call >> ')' >> *('[' >> value >> ']'))
			)[&todo_function];
						
			// Rvalue
			// scalar, arithm, func call
			rvalue = (
					!unary_oper >> (oper_expr | ident | scalar | func_call)
				|	incdec_oper >> ident
				|	ident >> incdec_oper
			);

			// Argument list for declaration
			// [type] name, ...
			args_decl = !type >> ident >> *(',' >> !type >> ident);

			// Return
			// return [value]
			ret_stmt = as_lower_d["return"] >> !rvalue[push_back_a(t.s)][&todo_return];
			
			// Break
			break_stmt = as_lower_d["break"][&todo_break];
			
			// Print
			print_stmt = as_lower_d["print"] >> (rvalue[push_back_a(t.s)]
				>> !(',' >> rvalue[push_back_a(t.s)] >> *(ch_p(',') >> rvalue[push_back_a(t.s)]))
			)[&todo_print];

			// Assigment
			// [type] name [ = value]
			assign_stmt = (!type[push_back_a(t.s)] >> ident[push_back_a(t.s)] >> '=' >> rvalue[push_back_a(t.s)])[&todo_assign];
			
			// Conditional statements
			// if - expr:
			//     ...
			// ;
			cond_stmt = (as_lower_d["if"] >> '-' >> rvalue[push_back_a(t.s)])[&todo_if] >> ':' >> expr
					>>	!(as_lower_d["elsif"] >> '-' >> rvalue >> ':' >> expr)
					>> 	!(as_lower_d["else"][&todo_else] >> ':' >> expr) >> ';';
			
			// Case statement
			// case var:
			//     when value:
			//     [when value:]
			//	... 
			//     ;
			//     [when value:
			//     	...
			//     ;]
			//     [others:
			//         ...
			//     ;]
			// ;
			case_stmt = (as_lower_d["case"] >> rvalue[push_back_a(t.s)] >> ':' 
					>> 	+(as_lower_d["when"] >> rvalue[push_back_a(t.s)] >> ':' 
						>> 	*(as_lower_d["when"] >> rvalue[push_back_a(t.s)] >> ':') >> *expr_def >> ';')
					>> 	!(as_lower_d["others"] >> ':' >> *expr_def >> ';') >> ';')[&todo_case];

			// Function declaration
			// def [type] name [- [type] varname, ...] :
			//     ...
			// ;
			def_stmt = (as_lower_d["def"] >> !type[push_back_a(t.s)] >> ident[push_back_a(t.s)] >> !('-' >> args_decl[push_back_a(t.s)]))[&todo_def] >> ':' >> *expr_def >> ';';
			
			// Variable declaration
			// var name [ [ = value ], ...]
			// type name [ [ = value ], ...]
			var_stmt = ((as_lower_d["var"] | type[push_back_a(t.s)])
				>> ident[push_back_a(t.s)] >> !(ch_p('=') >> rvalue[push_back_a(t.s)])
				>> *(ch_p(',') >> ident[push_back_a(t.s)] >> !(ch_p('=') >> rvalue[push_back_a(t.s)]))
			)[&todo_variable];
			
			// Loop statement
			// loop - expr:
			//     ...
			// ;
			// or
			// loop - var i = initial value, limit [, increment] :
			//     ...
			// ;
			loop_stmt = ((as_lower_d["loop"] >> '-' >> rvalue[push_back_a(t.s)])[&todo_loop] >> ':' >> expr >> ';')
					|	((as_lower_d["loop"] >> '-' >> var_stmt >> ',' >> rvalue[push_back_a(t.s)] >> !(ch_p(',') >> rvalue[push_back_a(t.s)]))[&todo_loop] >> ':' >> expr >> ';');
					
			// Class statement
			// cls Name:
			//     ...
			// ;
			class_stmt = (as_lower_d["cls"] >> ident[push_back_a(t.s)] >> ':')[&todo_class] >> expr_cls >> ';';
			
			// Visibility
			visibility = (as_lower_d["public"] | as_lower_d["private"] | as_lower_d["protected"]) >> ':';
			
			// Valid expression within a class
			expr_cls = *(!visibility >> (var_stmt | def_stmt));
			
			// Namespace statement
			// namespace foo:
			//     ...
			// ;
			namespace_stmt = (as_lower_d["namespace"] >> (ident >> *(str_p("::") >> ident))[push_back_a(t.s)])[&todo_namespace] >> ':' >> expr >> ';';
			
			// Valid expression within a function
			expr_def = (
					assign_stmt 
				|	ret_stmt
				| 	cond_stmt
				|	print_stmt
				|	case_stmt
				|	break_stmt
				|	loop_stmt
				|	var_stmt
				|	func_call
			);
						
			// Expression
			expr = *(
					def_stmt
				|	expr_def
				|	class_stmt
				|	namespace_stmt
				|	func_call
			);
			
			start_parser = expr >> end_p;
		}

		rule<ScannerT> type, ident, oper, oper_expr, rvalue, scalar, assign, ret, visibility;
		rule<ScannerT> args_decl, args_call, expr, expr_def, value, func_call, expr_cls;
		rule<ScannerT> arith_oper, logical_oper, comp_oper, bitwise_oper, unary_oper, incdec_oper;
		rule<ScannerT> cond_stmt, break_stmt, print_stmt, case_stmt, loop_stmt, var_stmt, class_stmt;
		rule<ScannerT> def_stmt, assign_stmt, ret_stmt, namespace_stmt, start_parser;
		
		rule<ScannerT> const& start() const { return start_parser; }
	};
};

struct TodoParserSkip : public grammar<TodoParserSkip> {
	template <typename ScannerT>
	struct definition {
		definition(TodoParserSkip const& self)
		{
			skip =  space_p
				|	"//" >> *(anychar_p - '\n') >> '\n'
				|	"/*" >> *(anychar_p - "*/") >> "*/"
				;
		}

		rule<ScannerT> skip;
		rule<ScannerT> const& start() const { return skip; }
	};
};
    
int main(int argc, char *argv[])
{
	ifstream script(argc > 1 ? argv[1] : "example.tdo");
	std::string source, line;
	TodoGrammar grammar;
	TodoParserSkip const TodoParserSkip_p = TodoParserSkip();
	
	if (!script.is_open()) {
		std::cout << "Cannot open file!" << std::endl;
		return 0;
	}
	
	while (!script.eof()) {
		getline(script, line);
		source += line + '\n';
	}
	script.close();
    
	// cout << source << endl;
	// cout << "---------------------------" << endl;

	if (!parse(source.c_str(), grammar, TodoParserSkip_p).full) {
		std::cout << "Parser failed!" << std::endl;
	}
	else {
		std::cout << "Done." << std::endl;
	}
	return 0;
}
